/*
Autor: Alexander Savochkin, 2010
New BSD License
*/

#pragma once

//TODO: objects are copyied by value (see Configuration methods implementation)

#include <string>
#include <vector>
#include <utility>
#include <algorithm>
#include <iterator>
#include <map>
#include <boost/optional.hpp>
#include <boost/property_tree/ptree.hpp>

using std::pair;
using std::string;
using std::vector;
using std::map;
using boost::property_tree::ptree;
using boost::optional;

namespace SRCLF {
namespace Config {

	typedef vector<string> ConfigValue;
	typedef pair<string, ConfigValue> KeyValue;
	
	/**
	@class Section
	@brief class Section represents section in config file
	@detailed configuration data grouped into named sections. Each section contains options related to some program entity (such as channel, input, output, e.t.c. )
	Configuration represented as key-value pairs. Config sections may be inherited. If key doesn't exist in section, but exists in parent sections, corresponding key-value pair
	will be imported from parent section.
	*/	
	class Section
	{
	public:
	  	///Type of configured entity
		enum Type {channel, input, output, regulator, resource, server};

		size_t keyCount(const string& key) const;	
		const string& getScalarByKey(const string& key) const;  //throws exception ... if key odes not exist or there are multiple values 
		const vector<string>& getAllValuesByKey(const string& key) const; //throws exception ... if key odes not exist

		void fillConfigSection(const string& stype, const ptree &section_subtree);

		///Section::Type -> std::string conversion. Helpful when writing config to file
		static void type2Name(Type type, string& name);
		///std::string -> Section::Type conversion. Helpful when reading config to file		
		static Type name2Type(const string& name);

		const string& getName() const { return name; }
		const boost::optional<string>& getParent() const { return parent; }
		Type getType() const { return type; }

		string getClassName() const;

	private:
		friend class Configuration;

		//parent_section inheritance must be already processed
		void applyInheritanceFromSection(Section& parent_section);

		Type type;	
		string name;
		optional<string> parent;
		optional<string> class_name;
		map<string, ConfigValue> attributes;
	};

	/**
	@class Configuration
	*/	
	class Configuration
	{
	public:
		typedef pair<Section::Type, string> SectionsKey;
		typedef pair< SectionsKey, Section> SectionsKeyVal;
		typedef map< SectionsKey, Section> Sections;
		Sections sections;
		//Initializers
		///Reads configuration file specified by filename		
		void readConfigFile( const string& filename );
		///Reads configuration file from istream		
		void readConfig( std::istream& istr );
		
		void initConfig( const ptree& property_tree );

		///Process inheritance for all sections
		void processInheritace();

		///Get all sections
		const Sections& getSections() const { return sections; }
		///Get section specified by name
		const Section& getSection(Section::Type type, const string& name) const;
	private:
		void processInheritaceForSection(Section& parent_section);	//parent_section may be not processed. In this case function will be called recursively

		Sections& _getSections() { return sections; }
		Section& _getSection(Section::Type type, const string& name);
	};
} //namespace Config
} //namespace SRCLF

